//
//  EMSSkillPlannerWindowController.m
//  EVE Mac Suite
//
//  Created by Vadim Lozko on 2/13/09.
//  Copyright 2009 __Vadim__. All rights reserved.
//

#import "EMSSkillPlannerWindowController.h"
#import "EMSSkillTrainingInfo.h"
#import "EMSSkillPlannerSkillBranchView.h"
#import "EMSSkillPlannerParser.h"
#import "StringToAttributedStringValueTransformer.h"
#import "EMSCharacterDataController.h"
#import "EVECDCharacter.h"
#import "EVECDAbstractSkill.h"
#import "EVECDLearnedSkill.h"
#import "EVECDSkill.h"
#import "EVECDSkillPlan.h"
#import "EVECDSkillPlanItem.h"
#import "EVEAPICoreDataController.h"
#import "EVEAPISkillTree.h"

@interface EMSSkillPlannerWindowController (PrivateMethods)

-(void)filterExistingPlannedSkills;

@end


@implementation EMSSkillPlannerWindowController

@synthesize delegate;

-(id)initWithCharacterDataController:(EMSCharacterDataController*)aCharacter
{
	if(self = [super initWithWindowNibName:@"SkillPlanner"])
	{
		dataController = [aCharacter retain];
		if([dataController.managedObjectCharacter.skillPlans count] == 0)
		{
			EVECDSkillPlan* skillPlan = [[NSEntityDescription insertNewObjectForEntityForName:@"EVECDSkillPlan" 
																			inManagedObjectContext:EVECDContext] retain];
			skillPlan.planFor = dataController.managedObjectCharacter;
		}
	}
	[[NSNotificationCenter defaultCenter] addObserver:self 
											 selector:@selector(skillQueueUpdateDidFinish:) 
												 name:CHARACTER_SKILL_QUEUE_API_TYPE
											   object:nil];
	return self;
}

-(void)awakeFromNib
{
	[self filterExistingPlannedSkills];
	[[self window] setDelegate:self];
	[plannedSkillsTableView registerForDraggedTypes:[NSArray arrayWithObject:SKILL_PLANNER_SKILL_AND_LEVEL_DRAGGED_TYPE]];
	StringToAttributedStringValueTransformer* valTransformer = [[[StringToAttributedStringValueTransformer alloc] init] autorelease];
	[NSValueTransformer setValueTransformer:valTransformer forName:@"StringToAttributedStringValueTransformer"];
	[branchView bind:@"contentSet"
			 toObject:groupsAndSkillsTreeController
		  withKeyPath:@"selection"
			  options:nil];
}

-(void)dealloc
{
	[dataController release];
	[super dealloc];
}

-(void)windowWillClose:(NSNotification *)notification
{
	if([delegate respondsToSelector:@selector(windowWillClose:)])
	{
		[delegate windowWillClose:notification];
	}
	[[EVEAPICoreDataController coreDataController] saveData];
	[self autorelease];
}

#pragma mark Other?

-(BOOL)validateMenuItem:(NSMenuItem*)menuItem
{
	SEL theAction = [menuItem action];
	if(theAction == @selector(setSkillAndLevel:))
	{
		if([[plannedSkillsArrayController selectedObjects] count] > 0)
		{
			EVECDSkillPlanItem* currentSkillPlanItem = [[plannedSkillsArrayController selectedObjects] objectAtIndex:0];
			NSPredicate* skillNamePredicate = [NSPredicate predicateWithFormat:@"learnedSkill.skill.name like %@", 
											   currentSkillPlanItem.learnedSkill.skill.name];
			NSArray* planItemsWithLearnedSkill = [[plannedSkillsArrayController arrangedObjects] filteredArrayUsingPredicate:skillNamePredicate];
			NSExpression* trainingToLevelExpression = [NSExpression expressionForKeyPath:@"trainingToLevel"];
			NSExpression* maxFunctionExpression = [NSExpression expressionForFunction:@"max:"
																			arguments:[NSArray arrayWithObject:trainingToLevelExpression]];
			int maxTrainingToLevel = [[maxFunctionExpression expressionValueWithObject:planItemsWithLearnedSkill context:nil] intValue];
			int menuItemValue = [[menuItem title] intValue];
			if(menuItemValue > maxTrainingToLevel)
			{
				return YES;
			} else {
				return NO;
			}
		} else {
			return NO;
		}
	} else {
		return NO;
	}
}

#pragma mark KVC Methods

-(EVECDCharacter*)character
{
	return [dataController managedObjectCharacter];
}

-(NSSet*)skills
{
	return [[EVEAPISkillTree apiSkillTree] skillGroups];
}

-(NSManagedObjectContext*)managedObjectContext
{
	return EVECDContext;
}

-(NSArray*)nameSortDescriptors
{
	return [EMSGeneralUtils nameSortDescriptors];
}

-(NSArray*)positionSortDescriptors
{
	return [EMSGeneralUtils positionSortDescriptors];
}

#pragma mark IBActions

-(IBAction)addNewSkillPlan:(id)sender
{
	EVECDSkillPlan* skillPlan = [[NSEntityDescription insertNewObjectForEntityForName:@"EVECDSkillPlan" 
																	inManagedObjectContext:EVECDContext] retain];
	skillPlan.planFor = dataController.managedObjectCharacter;
}

-(IBAction)removeSkillPlan:(id)sender
{
	if([[skillPlansArrayController selectedObjects] count] > 0)
	{
		EVECDSkillPlan* selectedSkillPlan = [[skillPlansArrayController selectedObjects] objectAtIndex:0];
		NSMutableSet* skillPlans = [[dataController managedObjectCharacter] mutableSetValueForKey:@"skillPlans"];
		[skillPlans removeObject:selectedSkillPlan];
		[EVECDContext deleteObject:selectedSkillPlan];
	}
}

-(IBAction)setSkillAndLevel:(NSMenuItem*)sender
{
	if([[plannedSkillsArrayController selectedObjects] count] > 0)
	{
		EVECDSkillPlanItem* currentSkillPlanItem = [[plannedSkillsArrayController selectedObjects] objectAtIndex:0];
		int indexOfSkillPlanItem = [[plannedSkillsArrayController arrangedObjects] indexOfObject:currentSkillPlanItem];
		int setToLevel = [[sender title] intValue];

		EMSSkillPlannerTreeNode* node = [EMSSkillTrainingInfo generateTreeForSkill:currentSkillPlanItem.learnedSkill 
																			 level:setToLevel];
		[parser  parseTree:node	atProposedRow:(indexOfSkillPlanItem + 1)];	
		[currentSkillPlanItem.partOfSkillPlan recalculateTrainingTimes];
		[plannedSkillsArrayController rearrangeObjects];
	}
}

-(IBAction)removeSkillPlanItem:(id)sender
{
	if([[plannedSkillsArrayController selectedObjects] count] > 0)
	{
		EVECDSkillPlanItem* skillPlanItem = [[plannedSkillsArrayController selectedObjects] objectAtIndex:0];
		EMSSkillPlannerTreeNode* skillNode = [EMSSkillTrainingInfo generateTreeForSkill:skillPlanItem.learnedSkill 
																				  level:skillPlanItem.learnedSkill.level.intValue];
		[parser deleteSkillPlanItem:skillPlanItem withRepresentativeNode:skillNode];
	}
}

-(IBAction)moveSkillPlanItemDown:(id)sender
{
	if([[skillPlansArrayController selectedObjects] count] && [[plannedSkillsArrayController selectedObjects] count])
	{
		NSArray* skills = [plannedSkillsArrayController arrangedObjects];
		EVECDSkillPlanItem* selectedItem = [[plannedSkillsArrayController selectedObjects] objectAtIndex:0];
		int itemIndex = [skills indexOfObject:selectedItem];
		if(itemIndex + 1 == [skills count])
		{
			return;
		}
		EVECDSkillPlanItem* nextPlanItem = (EVECDSkillPlanItem*)[skills objectAtIndex:(itemIndex + 1)];
		EMSSkillPlannerTreeNode* nextItemSkillBranch = [EMSSkillTrainingInfo generateTreeForSkill:nextPlanItem.learnedSkill 
																							level:nextPlanItem.trainingToLevel.intValue];
		[parser moveUpSkillPlanItem:nextPlanItem withRepresentativeNode:nextItemSkillBranch];
		[plannedSkillsArrayController rearrangeObjects];
	}
}

-(IBAction)moveSkillPlanItemUp:(id)sender
{
	if([[skillPlansArrayController selectedObjects] count] && [[plannedSkillsArrayController selectedObjects] count])
	{
		NSArray* skills = [plannedSkillsArrayController arrangedObjects];
		EVECDSkillPlanItem* selectedItem = [[plannedSkillsArrayController selectedObjects] objectAtIndex:0];
		int itemIndex = [skills indexOfObject:selectedItem];
		if(itemIndex == 0)
		{
			return;
		}
		EMSSkillPlannerTreeNode* currentItemSkillBranch = [EMSSkillTrainingInfo generateTreeForSkill:selectedItem.learnedSkill 
																							   level:selectedItem.trainingToLevel.intValue];
		[parser moveUpSkillPlanItem:selectedItem withRepresentativeNode:currentItemSkillBranch];
		[plannedSkillsArrayController rearrangeObjects];
	}
}

#pragma mark Table DataSource / Delegate Methods

//First several aren't required but are there to get rid of run time warnings.
-(NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {return 0;}
-(id)outlineView:(NSOutlineView*)outlineView objectValueForTableColumn:(NSTableColumn*)tableColumn byItem:(id)item {return nil;}
-(NSInteger)numberOfRowsInTableView:(NSTableView*)aTableView {return 0;}
-(id)tableView:(NSTableView*)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(NSInteger)rowIndex {return nil;}

-(BOOL)outlineView:(NSOutlineView*)inOutlineView writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard
{
	[pboard declareTypes:[NSArray arrayWithObject:SKILL_PLANNER_SKILL_AND_LEVEL_DRAGGED_TYPE] owner:nil];
	if([[[[[items objectAtIndex:0] representedObject] entity] name] isEqualToString:@"EVECDSkill"])
	{
		NSIndexPath *pathToDraggedNode = [[items objectAtIndex:0] indexPath];
		NSData *indexPathData = [NSKeyedArchiver archivedDataWithRootObject:pathToDraggedNode];
		[pboard setData:indexPathData forType:SKILL_PLANNER_SKILL_AND_LEVEL_DRAGGED_TYPE];
		return YES;
	} else {
		return NO;
	}
}

- (NSDragOperation)tableView:(NSTableView*)tv 
				validateDrop:(id <NSDraggingInfo>)info 
				 proposedRow:(int)row 
	   proposedDropOperation:(NSTableViewDropOperation)operation
{
	if([[skillPlansArrayController selectedObjects] count] == 1 && operation == NSTableViewDropAbove)
	{
		return NSDragOperationGeneric;
	} else {
		return NSDragOperationNone;
	}
}

-(BOOL)tableView:(NSTableView*)tableView 
	  acceptDrop:(id <NSDraggingInfo>)info 
			 row:(int)row 
   dropOperation:(NSTableViewDropOperation)operation
{
	if([[skillPlansArrayController selectedObjects] count] == 1)
	{
		NSIndexPath *droppedIndexPath = [NSKeyedUnarchiver unarchiveObjectWithData:[[info draggingPasteboard] 
																					dataForType:SKILL_PLANNER_SKILL_AND_LEVEL_DRAGGED_TYPE]];
		id treeRoot = [groupsAndSkillsTreeController arrangedObjects];
		EMSSkillPlannerTreeNode *node = (EMSSkillPlannerTreeNode*)[treeRoot descendantNodeAtIndexPath:droppedIndexPath];
		NSSet* learnedSkillsForSelectedSkill = ((EVECDSkill*)[node representedObject]).partOfLearnedSkill;
		NSPredicate* characterNamePredicate = [NSPredicate predicateWithFormat:@"partOfLearnedGroup.learnedBy.name like %@", self.character.name];
		EVECDLearnedSkill* matchingLearnedSkill = [[learnedSkillsForSelectedSkill filteredSetUsingPredicate:characterNamePredicate] anyObject];
		EVECDSkillPlan* skillPlan = [[skillPlansArrayController selectedObjects] objectAtIndex:0];
		NSPredicate* planItemsWithLearnedSkillPredicate = [NSPredicate predicateWithFormat:@"learnedSkill.skill.name like %@", 
														   matchingLearnedSkill.skill.name];
		NSSet* planItemsWithLearnedSkill = [skillPlan.skillPlanItems filteredSetUsingPredicate:planItemsWithLearnedSkillPredicate];
		EMSSkillPlannerTreeNode* rootNode;
		if([planItemsWithLearnedSkill count] == 0)
		{
			if(matchingLearnedSkill.level.intValue == 5)
			{
				return NO;
			}
			rootNode = [EMSSkillTrainingInfo generateTreeForSkill:matchingLearnedSkill level:(matchingLearnedSkill.level.intValue + 1)];
		} else {
			NSExpression* trainingToLevelExpression = [NSExpression expressionForKeyPath:@"trainingToLevel"];
			NSExpression* maxFunctionExpression = [NSExpression expressionForFunction:@"max:"
																			arguments:[NSArray arrayWithObject:trainingToLevelExpression]];
			int maxTrainingToLevel = [[maxFunctionExpression expressionValueWithObject:planItemsWithLearnedSkill context:nil] intValue];
			if(maxTrainingToLevel == 5)
			{
				return NO;
			} else {
				rootNode = [EMSSkillTrainingInfo generateTreeForSkill:matchingLearnedSkill level:(maxTrainingToLevel + 1)];
			}
		}
		
		BOOL accept = [parser parseTree:rootNode atProposedRow:row];
		if(accept)
		{
			[skillPlan recalculateTrainingTimes];
			[plannedSkillsArrayController rearrangeObjects];
		}
		return accept;
	}
	return NO;	
}
@end

@implementation EMSSkillPlannerWindowController (PrivateMethods)

-(void)skillQueueUpdateDidFinish:(NSNotification*)notification
{
	if([[[notification userInfo] objectForKey:CHAR_INFO_DICT_NAME] isEqualToString:[self character].name])
	{
		[self filterExistingPlannedSkills];
	}
}

-(void)filterExistingPlannedSkills
{
	for(EVECDSkillPlan* skillPlan in self.character.skillPlans)
	{
		NSMutableArray* skillPlanItemsToDelete = [NSMutableArray array];
		for(EVECDSkillPlanItem* skillPlanItem in skillPlan.skillPlanItems)
		{
			if(skillPlanItem.trainingToLevel.intValue <= skillPlanItem.learnedSkill.level.intValue)
			{
				[skillPlanItemsToDelete addObject:skillPlanItem];
			}
		}
		for(EVECDSkillPlanItem* removedSkillPlanItem in skillPlanItemsToDelete)
		{
#ifdef DEBUG
			NSLog(@"Found skill already trained. Deleting.");
#endif
			EMSSkillPlannerTreeNode* node = [EMSSkillTrainingInfo generateTreeForSkill:removedSkillPlanItem.learnedSkill 
																				 level:removedSkillPlanItem.trainingToLevel.intValue];
			[parser deleteSkillPlanItem:removedSkillPlanItem withRepresentativeNode:node];
		}
	}
}

@end

